/*
 * 描          述:  <描述>
 * 修  改   人:  PengQingyang
 * 修改时间:  2012-11-4
 * <修改描述:>
 */
package com.tx.core.mybatis.support;

import cn.hutool.core.lang.generator.SnowflakeGenerator;
import cn.hutool.core.lang.generator.UUIDGenerator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.tx.core.exceptions.SILException;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.mybatis.config.MybatisGlobalConfig;
import com.tx.core.paged.model.PagedList;
import com.tx.core.paged.model.impl.PagedListImpl;
import com.tx.core.util.MessageUtils;
import com.tx.core.util.TableInfoUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.dao.support.PersistenceExceptionTranslator;

import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * mybatis数据库查询类<br/>
 *
 * @author PengQingyang
 * @version [20240301]
 * @since [20240301]
 */
public class MyBatisDaoSupport implements InitializingBean {

    /** 默认的uuid生成方案 */
    private static final UUIDGenerator uuidGenerator = new UUIDGenerator();

    private static final SnowflakeGenerator snowflakeGenerator = new SnowflakeGenerator();

    /** 日志记录器 */
    protected final Log log = LogFactory.getLog(getClass());

    /** sqlSessionTemplate */
    private SqlSessionTemplate sqlSessionTemplate;

    /** <默认构造函数> */
    public MyBatisDaoSupport() {
        super();
    }

    /** <默认构造函数> */
    public MyBatisDaoSupport(SqlSessionTemplate sqlSessionTemplate) {
        super();
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    /** afterPropertiesSet */
    @Override
    public void afterPropertiesSet() {
        AssertUtils.notNull(this.sqlSessionTemplate,
                "sqlSessionTemplate is null.");
    }

    /**
     * 保存实体<br/>
     * 如果存在，则执行更新操作，如果不存在，执行插入操作
     *
     * @param entity          [实体]
     * @param updateWrapper   [构建更新字段]
     * @param params          [params]
     * @param findStatement   查询所用到的查询statement
     * @param insertStatement 插入所用到的查询statement
     * @param updateStatement 更新所用到的查询statement
     * @param pkPropertyName  [主键属性名]
     */
    public <T> void save(T entity, Wrapper<T> updateWrapper, Map<String, Object> params,
                         String findStatement, String insertStatement,
                         String updateStatement, String pkPropertyName) {
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(updateWrapper, "updateWrapper is null.");
        AssertUtils.notEmpty(findStatement, "findStatement is empty.");
        AssertUtils.notEmpty(insertStatement, "insertStatement is empty.");
        AssertUtils.notEmpty(updateStatement, "updateStatement is empty.");
        AssertUtils.notEmpty(pkPropertyName, "pkPropertyName is empty.");

        BeanWrapper bw = PropertyAccessorFactory
                .forBeanPropertyAccess(entity);
        Object pkValue = bw.getPropertyValue(pkPropertyName);
        if (ObjectUtil.isEmpty(pkValue)) {
            //如果指定了keyProperty,并且主键为空，则自动生成主键并插入
            autoSetEntityPKWithGenerator(entity, pkPropertyName);
            //插入实体
            insert(insertStatement, entity);
        } else {
            //查找：期对应的实体是否存在
            Object resObj = find(findStatement, entity);
            if (resObj == null) {
                insert(insertStatement, entity);
            } else {
                update(updateStatement, entity, updateWrapper, params);
            }
        }
    }

    /**
     * 保存实体<br/>
     * 如果存在，则执行更新操作，如果不存在，执行插入操作
     *
     * @param entity                 [实体]
     * @param updateWrapperGenerator [构建更新字段]
     * @param params                 [params]
     * @param findStatement          查询所用到的查询statement
     * @param insertStatement        插入所用到的查询statement
     * @param updateStatement        更新所用到的查询statement
     * @param pkPropertyName         [主键属性名]
     */
    public <T> void save(T entity, Function<T, Wrapper<T>> updateWrapperGenerator, Map<String, Object> params,
                         String findStatement, String insertStatement,
                         String updateStatement, String pkPropertyName) {
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(updateWrapperGenerator, "updateWrapperGenerator is null.");
        AssertUtils.notEmpty(findStatement, "findStatement is empty.");
        AssertUtils.notEmpty(insertStatement, "insertStatement is empty.");
        AssertUtils.notEmpty(updateStatement, "updateStatement is empty.");
        AssertUtils.notEmpty(pkPropertyName, "pkPropertyName is empty.");

        BeanWrapper bw = PropertyAccessorFactory
                .forBeanPropertyAccess(entity);
        Object pkValue = bw.getPropertyValue(pkPropertyName);
        if (ObjectUtil.isEmpty(pkValue)) {
            //如果指定了keyProperty,并且主键为空，则自动生成主键并插入
            autoSetEntityPKWithGenerator(entity, pkPropertyName);
            //插入实体
            insert(insertStatement, entity);
        } else {
            //查找：期对应的实体是否存在
            Object resObj = find(findStatement, entity);
            if (resObj == null) {
                insert(insertStatement, entity);
            } else {
                update(updateStatement, entity, updateWrapperGenerator, params);
            }
        }
    }

    /**
     * 保存实体<br/>
     * 如果存在，则执行更新操作，如果不存在，执行插入操作
     *
     * @param entity          [实体]
     * @param findStatement   查询所用到的查询statement
     * @param insertStatement 插入所用到的查询statement
     * @param updateStatement 更新所用到的查询statement
     * @param pkPropertyName  [主键属性名]
     * @param mapGenerator    [构建更新字段]
     */
    public <T> void save(T entity, Function<T, Map<String, Object>> mapGenerator, String findStatement,
                         String insertStatement, String updateStatement, String pkPropertyName) {
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(mapGenerator, "mapGenerator is null.");
        AssertUtils.notEmpty(findStatement, "findStatement is empty.");
        AssertUtils.notEmpty(insertStatement, "insertStatement is empty.");
        AssertUtils.notEmpty(updateStatement, "updateStatement is empty.");
        AssertUtils.notEmpty(pkPropertyName, "pkPropertyName is empty.");

        BeanWrapper bw = PropertyAccessorFactory
                .forBeanPropertyAccess(entity);
        Object pkValue = bw.getPropertyValue(pkPropertyName);
        if (ObjectUtil.isEmpty(pkValue)) {
            //如果指定了keyProperty,并且主键为空，则自动生成主键并插入
            autoSetEntityPKWithGenerator(entity, pkPropertyName);
            //插入实体
            insert(insertStatement, entity);
        } else {
            //查找：期对应的实体是否存在
            Object resObj = find(findStatement, entity);
            if (resObj == null) {
                insert(insertStatement, entity);
            } else {
                update(updateStatement, entity, mapGenerator);
            }
        }
    }

    /**
     * 批量保存实体列表。该方法会首先尝试更新已存在的实体，如果不存在，则插入新实体。
     *
     * @param entityList             需要批量保存的实体列表。
     * @param updateWrapperGenerator 一个函数接口，用于根据实体生成更新条件Wrapper。
     * @param params                 用于动态SQL参数映射的键值对。
     * @param findStatement          用于查询实体是否存在的SQL或SQL片段。
     * @param insertStatement        用于插入新实体的SQL或SQL片段。
     * @param updateStatement        用于更新实体的SQL或SQL片段。
     * @param pkPropertyName         实体的主键属性名称。
     * @param <T>                    实体类型。
     */
    public <T> void batchSave(List<T> entityList, Function<T, Wrapper<T>> updateWrapperGenerator,
                              Map<String, Object> params, String findStatement, String insertStatement,
                              String updateStatement, String pkPropertyName) {
        batchSave(entityList, updateWrapperGenerator, params, findStatement, insertStatement, updateStatement,
                pkPropertyName, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, false);
    }


    /**
     * 批量保存实体列表。该方法根据提供的实体列表、更新包装器生成器、参数映射、查询语句、插入语句和更新语句，
     * 以及主键属性名称和是否使用批量执行器的标志来执行批量保存操作。该操作可以是插入新实体，或更新已存在的实体。
     *
     * @param entityList             需要批量保存的实体列表，不应为null。
     * @param updateWrapperGenerator 一个函数接口，用于根据实体生成更新语句的包装器。典型地，这会根据实体的当前状态和目标状态生成更新语句。
     * @param params                 一个包含批量保存操作中可能需要的参数的映射。
     * @param findStatement          用于查询实体是否已存在的SQL语句。
     * @param insertStatement        用于插入新实体的SQL语句。
     * @param updateStatement        用于更新已有实体的SQL语句。
     * @param pkPropertyName         实体的主键属性名称，用于标识和检索特定实体。
     * @param useBatchExecutor       标志位，当为true时，使用批量执行器来执行保存操作，提高效率。
     * @param <T>                    实体类型的泛型参数。
     */
    public <T> void batchSave(List<T> entityList, Function<T, Wrapper<T>> updateWrapperGenerator,
                              Map<String, Object> params, String findStatement, String insertStatement,
                              String updateStatement, String pkPropertyName,
                              boolean useBatchExecutor) {
        // 调用另一个重载方法，将批量保存的大小设置为默认值，并开启或不开启批量执行器
        batchSave(entityList, updateWrapperGenerator, params, findStatement, insertStatement, updateStatement,
                pkPropertyName, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, useBatchExecutor);
    }


    /**
     * 批量保存实体列表。
     * 遍历实体列表，对每个实体执行查找、插入或更新操作。如果实体的主键为空，则插入新记录；如果主键不为空，先尝试根据主键查找现有记录，若存在则更新，不存在则插入。
     * 执行方式可根据参数选择使用批量执行器或逐条执行。
     *
     * @param entityList             待保存的实体列表。
     * @param updateWrapperGenerator 用于生成更新语句的函数，根据实体生成对应的Wrapper。
     * @param params                 执行保存操作时可能需要的参数。
     * @param findStatement          根据主键查找实体的SQL语句。
     * @param insertStatement        插入实体的SQL语句。
     * @param updateStatement        更新实体的SQL语句。
     * @param pkPropertyName         实体的主键属性名称。
     * @param doFlushSize            执行刷新的频率，即每处理多少条数据刷新一次。
     * @param useBatchExecutor       是否使用批量执行器执行保存操作。
     * @param <T>                    实体类型。
     */
    public <T> void batchSave(List<T> entityList, Function<T, Wrapper<T>> updateWrapperGenerator,
                              Map<String, Object> params, String findStatement, String insertStatement,
                              String updateStatement, String pkPropertyName,
                              int doFlushSize, boolean useBatchExecutor) {
        // 判断入参有效性
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        AssertUtils.notEmpty(findStatement, "findStatement is empty.");
        AssertUtils.notEmpty(insertStatement, "insertStatement is empty.");
        AssertUtils.notEmpty(updateStatement, "updateStatement is empty.");
        AssertUtils.notEmpty(pkPropertyName, "pkPropertyName is empty.");
        // 校验并设置刷新频率
        if (doFlushSize <= 0) {
            doFlushSize = MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE;
        }

        // 根据是否使用批量执行器选择不同的保存逻辑
        if (useBatchExecutor) {
            // 使用批量执行器执行保存逻辑，根据实体主键的存在与否决定是插入还是更新
            SqlHelper.executeBatch(getSqlSessionFactory(), this.log, entityList, doFlushSize, (sqlSession,
                                                                                               entity) -> {
                // 处理单个实体的保存逻辑
                BeanWrapper bw = PropertyAccessorFactory
                        .forBeanPropertyAccess(entity);
                Object pkValue = bw.getPropertyValue(pkPropertyName);
                if (ObjectUtil.isEmpty(pkValue)) {
                    //如果指定了keyProperty,并且主键为空，则自动生成主键并插入
                    autoSetEntityPKWithGenerator(entity, pkPropertyName);
                    // 主键为空执行插入操作
                    sqlSession.insert(insertStatement, entity);
                } else {
                    // 主键不为空，先尝试查找实体，根据结果执行插入或更新操作
                    T res = sqlSession.selectOne(findStatement, entity);
                    if (res == null) {
                        sqlSession.insert(insertStatement, entity);
                    } else {
                        // 生成更新语句并执行
                        Map<String, Object> updateRow = new HashMap<>();
                        updateRow.put(Constants.ENTITY, entity);
                        if (updateWrapperGenerator != null) {
                            updateRow.put(Constants.WRAPPER, updateWrapperGenerator.apply(entity));
                        }
                        sqlSession.update(updateStatement, updateRow);
                    }
                }
            });
        } else {
            // 逐条执行保存逻辑
            for (int index = 0; index < entityList.size(); index++) {
                save(entityList.get(index), updateWrapperGenerator, params, findStatement, insertStatement,
                        updateStatement,
                        pkPropertyName);
            }
        }
    }

    /**
     * 批量保存实体列表。
     * 该方法会根据提供的实体列表、更新映射生成器、查询语句、插入语句和更新语句，以及主键属性名来执行批量保存操作。
     * 它会首先尝试更新已有记录，如果记录不存在，则插入新记录。
     *
     * @param entityList         要进行批量保存的实体列表，不应为null。
     * @param updateMapGenerator 一个函数接口，用于根据实体生成更新映射（即set语句中的值），不应为null。
     * @param findStatement      用于查询实体是否已存在的SQL语句，不应为null。
     * @param insertStatement    用于插入新实体的SQL语句，不应为null。
     * @param updateStatement    用于更新已有实体的SQL语句，不应为null。
     * @param pkPropertyName     实体的主键属性名称，用于在更新和查询语句中标识实体，不应为null或空字符串。
     * @param <T>                实体类型的泛型参数。
     */
    public <T> void batchSave(List<T> entityList, Function<T, Map<String, Object>> updateMapGenerator,
                              String findStatement, String insertStatement, String updateStatement,
                              String pkPropertyName) {
        // 调用另一个重载方法，将批量保存的大小设置为默认值，并开启或不开启批量执行器
        batchSave(entityList, updateMapGenerator, findStatement, insertStatement, updateStatement,
                pkPropertyName, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, false);
    }


    /**
     * 批量保存实体列表。
     * 该方法会根据提供的实体列表、更新映射生成器、查询语句、插入语句、更新语句以及主键属性名，来执行批量保存操作。
     * 可以选择是否使用批量执行器来优化保存的效率。
     *
     * @param entityList         需要保存的实体列表，不应为空。
     * @param updateMapGenerator 一个函数接口，用于根据实体生成更新映射（即set语句的部分）。
     * @param findStatement      用于查询实体的SQL语句。
     * @param insertStatement    用于插入实体的SQL语句。
     * @param updateStatement    用于更新实体的SQL语句。
     * @param pkPropertyName     实体的主键属性名称。
     * @param useBatchExecutor   是否使用批量执行器来执行保存操作。
     * @param <T>                实体的类型。
     */
    public <T> void batchSave(List<T> entityList, Function<T, Map<String, Object>> updateMapGenerator,
                              String findStatement, String insertStatement, String updateStatement,
                              String pkPropertyName, boolean useBatchExecutor) {
        // 调用另一个重载方法，将批量保存的大小设置为默认值，并开启或不开启批量执行器
        batchSave(entityList, updateMapGenerator, findStatement, insertStatement, updateStatement,
                pkPropertyName, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, useBatchExecutor);
    }


    /**
     * 批量保存实体，根据实体是否存在主键来决定是插入还是更新。
     *
     * @param entityList         需要保存的实体列表
     * @param updateMapGenerator 用于生成更新语句的函数接口，接收一个实体，返回一个包含更新信息的Map
     * @param findStatement      用于查找实体的SQL语句
     * @param insertStatement    用于插入实体的SQL语句
     * @param updateStatement    用于更新实体的SQL语句
     * @param pkPropertyName     实体的主键属性名称
     * @param doFlushSize        执行刷新的频率，即每处理多少条数据后刷新一次数据库
     * @param useBatchExecutor   是否使用批量执行器来执行保存操作
     * @param <T>                实体类型
     */
    public <T> void batchSave(List<T> entityList, Function<T, Map<String, Object>> updateMapGenerator,
                              String findStatement, String insertStatement, String updateStatement,
                              String pkPropertyName, int doFlushSize, boolean useBatchExecutor) {
        // 判断入参有效性
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        AssertUtils.notEmpty(updateMapGenerator, "updateMapGenerator is empty.");
        AssertUtils.notEmpty(findStatement, "findStatement is empty.");
        AssertUtils.notEmpty(insertStatement, "insertStatement is empty.");
        AssertUtils.notEmpty(updateStatement, "updateStatement is empty.");
        AssertUtils.notEmpty(pkPropertyName, "pkPropertyName is empty.");
        // 校验并设置刷新频率
        if (doFlushSize <= 0) {
            doFlushSize = MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE;
        }

        // 根据是否使用批量执行器选择不同的保存逻辑
        if (useBatchExecutor) {
            // 使用批量执行器执行保存逻辑，根据实体主键的存在与否决定是插入还是更新
            SqlHelper.executeBatch(getSqlSessionFactory(), this.log, entityList, doFlushSize, (sqlSession,
                                                                                               entity) -> {
                // 处理单个实体的保存逻辑
                BeanWrapper bw = PropertyAccessorFactory
                        .forBeanPropertyAccess(entity);
                Object pkValue = bw.getPropertyValue(pkPropertyName);
                if (ObjectUtil.isEmpty(pkValue)) {
                    //如果指定了keyProperty,并且主键为空，则自动生成主键并插入
                    autoSetEntityPKWithGenerator(entity, pkPropertyName);
                    // 主键为空执行插入操作
                    sqlSession.insert(insertStatement, entity);
                } else {
                    // 主键不为空，先尝试查找实体，根据结果执行插入或更新操作
                    T res = sqlSession.selectOne(findStatement, entity);
                    if (res == null) {
                        sqlSession.insert(insertStatement, entity);
                    } else {
                        // 生成更新语句并执行
                        Map<String, Object> updateRow = updateMapGenerator.apply(entity);
                        updateRow.put(Constants.ENTITY, entity);
                        sqlSession.update(updateStatement, updateRow);
                    }
                }
            });
        } else {
            // 逐条执行保存逻辑
            for (int index = 0; index < entityList.size(); index++) {
                save(entityList.get(index), updateMapGenerator, findStatement, insertStatement,
                        updateStatement,
                        pkPropertyName);
            }
        }
    }


    /**
     * 插入前使用uuid为指定属性赋值，
     * 1、如果在sqlMap上指定了selectKey那最终会是selectKey生效
     *
     * @param statement      [sqlStatementId]
     * @param parameter      [实体实例]
     * @param pkPropertyName [主键属性名]
     */
    public void insertUseUUID(String statement, Object parameter,
                              String pkPropertyName) {
        insertWithPKGenerator(statement, parameter, pkPropertyName);
    }

    /**
     * 使用主键生成器在插入前为主键属性赋值。该方法支持的主键类型为String和Long。
     *
     * @param statement      指定的SQL语句ID，用于插入操作。
     * @param parameter      实体实例，将被插入到数据库。
     * @param pkPropertyName 主键属性的名称。
     * @throws IllegalArgumentException 如果statement或pkPropertyName为空，或者parameter为null，则抛出该异常。
     */
    public void insertWithPKGenerator(String statement, Object parameter,
                                      String pkPropertyName) {
        // 确保传入的statement和pkPropertyName不为空，parameter不为null
        AssertUtils.notEmpty(statement, "statement is empty.");
        AssertUtils.notEmpty(pkPropertyName, "pkPropertyName is empty.");
        AssertUtils.notNull(parameter, "parameter is null.");

        // 使用主键生成器自动设置实体的主键属性值
        autoSetEntityPKWithGenerator(parameter, pkPropertyName);
        // 根据parameter是否为null执行相应的插入操作
        if (parameter != null) {
            this.sqlSessionTemplate.insert(statement, parameter);
        } else {
            this.sqlSessionTemplate.insert(statement);
        }
    }

    /**
     * 插入对象到数据库。
     * 该方法使用提供的SQL语句和参数对象执行数据库插入操作。
     * 如果参数对象不为null，则会使用参数对象执行插入；否则，仅使用SQL语句执行插入。
     *
     * @param statement 表示SQL语句的字符串，不能为空。
     * @param parameter 要插入的实体实例，可以为null。
     */
    public void insert(String statement, Object parameter) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 根据parameter是否为null，选择不同的插入方式
        if (parameter != null) {
            this.sqlSessionTemplate.insert(statement, parameter);
        } else {
            this.sqlSessionTemplate.insert(statement);
        }
    }

    /**
     * 批量插入数据 <br/>
     * 1、数据批量插入，默认一次提交100条，当发生异常后继续提交异常行以后的数据，待集合全部进行提交后返回批量处理结果<br/>
     * 2、数据批量插入，如果需要回滚，当发生异常后，数据库异常即向外抛出，不会进行至全部执行后再抛出异常 <br/>
     * 此方法将给定的数据列表批量插入到数据库中，支持配置每次提交的条数和是否需要回滚。
     *
     * @param statement  指定的SQL语句ID，用于标识要执行的批量插入操作。
     * @param objectList 要插入的数据实体列表，列表中的每个对象都将被插入到数据库中。
     */
    public void batchInsert(String statement, List<?> objectList) {
        // 批量插入数据，使用默认的提交条数（100条）和不回滚配置
        batchInsert(statement, objectList, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, false);
    }

    /**
     * 批量插入数据
     * <br/>
     * 1、数据批量插入，默认一次提交100条，当发生异常后继续提交异常行以后的数据，待集合全部进行提交后返回批量处理结果<br/>
     * 2、数据批量插入，如果需要回滚，当发生异常后，数据库异常即向外抛出，不会进行至全部执行后再抛出异常 <br/>
     * <功能详细描述>请根据具体实现补充
     *
     * @param statement        [sql语句标识符]，用于指定要执行的SQL语句的标识符。
     * @param objectList       [实体列表]，包含需要插入的所有实体对象的列表。
     * @param useBatchExecutor [是否使用批量执行器]，控制是否使用批量执行策略进行数据插入。
     */
    public void batchInsert(String statement, List<?> objectList,
                            boolean useBatchExecutor) {
        // 批量插入数据，通过指定的sql语句标识符、实体列表和是否使用批量执行器来执行批量插入操作
        batchInsert(statement,
                objectList,
                MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE,
                useBatchExecutor);
    }

    /**
     * 批量插入数据
     *
     * @param statement        指定的SQL语句ID，用于插入操作
     * @param entityList       要插入的实体对象列表
     * @param doFlushSize      每次批量提交的条数，默认为MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE
     * @param useBatchExecutor 是否使用批量执行器。当设置为true时，将在异常发生时继续执行，否则停止执行。
     */
    // 批量插入方法
    public void batchInsert(String statement, List<?> entityList,
                            int doFlushSize, boolean useBatchExecutor) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果实体列表为空，则直接返回
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        // 如果指定的批量提交条数小于等于0，则使用默认值
        if (doFlushSize <= 0) {
            doFlushSize = MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE;
        }

        // 使用批量执行器进行数据插入，或者逐条插入数据
        // 如果使用批量执行器，将通过executeBatch方法执行批量插入
        if (useBatchExecutor) {
            SqlHelper.executeBatch(getSqlSessionFactory(), this.log, entityList, doFlushSize, (sqlSession,
                                                                                               entity) -> {
                sqlSession.insert(statement, entity);
            });
        } else {
            // 如果不使用批量执行器，则遍历列表，逐条插入数据
            for (int index = 0; index < entityList.size(); index++) {
                insert(statement, entityList.get(index));
            }
        }
    }

    /**
     * 批量插入数据，主键字段自动生成并插入。<br/>
     * 1、此方法默认一次提交100条数据。如果发生异常，会继续提交异常行之后的数据，直到整个数据集合全部提交完毕，然后返回批量处理结果。<br/>
     * 2、如果需要数据插入过程支持回滚，当发生异常时，数据库异常会立即被抛出，不会等到所有数据都执行完毕后再抛出异常。<br/>
     *
     * @param statement      指定的SQL语句ID。
     * @param objectList     要插入的数据实体列表。
     * @param pkPropertyName 主键属性的名称。
     * @deprecated 已过时，请使用其他方法替代。
     */
    public void batchInsertUseUUID(String statement, List<?> objectList,
                                   String pkPropertyName) {
        batchInsertWithPKGenerator(statement,
                objectList,
                pkPropertyName);
    }


    /**
     * 批量插入数据，主键字段自动生成并插入。<br/>
     * 该方法提供了批量插入数据的功能，其中主键字段会自动生成。具体特点如下：<br/>
     * 1、数据批量插入时，默认每次提交100条。如果在执行过程中发生异常，会继续提交异常行之后的数据，直到全部数据处理完毕后返回批量处理结果。<br/>
     * 2、如果需要数据插入过程支持回滚，当发生异常时，会立即抛出数据库异常，而不会等到所有数据都执行完毕后再抛出异常。<br/>
     *
     * @param statement      指定的SQL语句ID，用于标识要执行的插入语句。
     * @param objectList     要插入的数据实体列表。
     * @param pkPropertyName 主键属性的名称，用于标识实体中的主键字段。
     *                       <p>
     *                       注意：该方法已弃用，请使用其他方法替代。
     */
    public void batchInsertWithPKGenerator(String statement, List<?> objectList,
                                           String pkPropertyName) {
        // 使用默认的提交条数和不支持回滚的方式，调用另一个具有更多参数控制的批量插入方法
        batchInsertWithPKGenerator(statement,
                objectList,
                pkPropertyName,
                MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE,
                false);
    }


    /**
     * 批量插入<br/>
     * 该方法使用UUID作为主键生成策略进行批量插入。已弃用，请使用其他方法代替。<br/>
     * <功能详细描述>
     *
     * @param statement        SQL语句标识符，用于指定要执行的批量插入语句
     * @param objectList       实体对象列表，包含了需要插入的数据
     * @param pkPropertyName   主键属性的名称，用于指定对象中主键属性的字段名
     * @param useBatchExecutor 是否使用批量执行器。如果为true，则使用批量执行策略；反之，则不使用。
     */
    public void batchInsertUseUUID(String statement, List<?> objectList,
                                   String pkPropertyName, boolean useBatchExecutor) {
        // 使用主键生成器进行批量插入
        batchInsertWithPKGenerator(statement,
                objectList,
                pkPropertyName,
                useBatchExecutor);
    }


    /**
     * 批量插入<br/>
     * 该方法用于批量插入数据，并且支持自动生成主键。这是一个过时的方法，请使用其他方式来实现批量插入。<br/>
     * <功能详细描述>
     *
     * @param statement        [sqlStatementId] SQL语句的标识符
     * @param objectList       [实体list] 要插入的数据实体列表
     * @param pkPropertyName   [主键属性名] 实体对象中主键属性的名称
     * @param useBatchExecutor [参数说明] 是否使用批量执行器来执行插入操作
     */
    public void batchInsertWithPKGenerator(String statement, List<?> objectList,
                                           String pkPropertyName, boolean useBatchExecutor) {
        // 调用另一个重载方法，以固定的刷新大小执行批量插入
        batchInsertWithPKGenerator(statement,
                objectList,
                pkPropertyName,
                MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE,
                useBatchExecutor);
    }


    /**
     * <pre>
     * 批量插入数据
     *   主键字段自动生成并插入。此方法已弃用，请使用其他方式。
     * </pre>
     *
     * @param statement        [sqlStatementId]，用于指定要执行的批量插入语句的ID。
     * @param objectList       [实体list]，包含需要插入的所有实体对象。
     * @param pkPropertyName   [主键属性名]，指定实体对象中的主键属性名称。
     * @param doFlushSize      [刷新大小]，指定在执行批量插入前，收集多少条数据后进行一次刷新。
     * @param useBatchExecutor 是否使用批量执行器。如果为true，则使用批量执行器执行插入操作。
     */
    public void batchInsertUseUUID(String statement, List<?> objectList,
                                   String pkPropertyName, int doFlushSize, boolean useBatchExecutor) {
        // 使用主键生成器进行批量插入
        batchInsertWithPKGenerator(statement, objectList, pkPropertyName, doFlushSize, useBatchExecutor);
    }


    /**
     * 批量插入数据，主键字段自动生成并插入。
     * 该方法会将实体列表中的所有实体插入到数据库，对于主键字段，会自动生成其值。
     *
     * @param statement        执行的SQL语句ID。
     * @param objectList       要插入的实体列表。
     * @param pkPropertyName   主键属性的名称。
     * @param doFlushSize      每执行一批插入操作前，列表的大小。
     * @param useBatchExecutor 是否使用批量执行器来执行插入操作。
     */
    public void batchInsertWithPKGenerator(String statement, List<?> objectList,
                                           String pkPropertyName, int doFlushSize, boolean useBatchExecutor) {
        // 确保传入的SQL语句ID、实体列表和主键属性名称不为空
        AssertUtils.notEmpty(statement, "statement is empty.");
        if (CollectionUtils.isEmpty(objectList)) {
            return;
        }
        AssertUtils.notEmpty(pkPropertyName, "pkPropertyName is empty.");

        // 如果指定的每批操作大小小于等于0，则使用默认值
        if (doFlushSize <= 0) {
            doFlushSize = MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE;
        }

        // 使用批量执行器或循环来处理插入操作
        if (useBatchExecutor) {
            // 使用批量执行器执行插入操作
            SqlHelper.executeBatch(getSqlSessionFactory(), this.log, objectList, doFlushSize, (sqlSession,
                                                                                               entity) -> {
                // 自动为实体设置主键值
                autoSetEntityPKWithGenerator(entity, pkPropertyName);
                sqlSession.insert(statement, entity);
            });
        } else {
            // 循环处理每个实体的插入操作
            for (int index = 0; index < objectList.size(); index++) {
                insertWithPKGenerator(statement, objectList.get(index), pkPropertyName);
            }
        }
    }

    /**
     * 删除对象
     *
     * @param statement [sqlStatementId]
     * @param parameter [实体实例]
     */
    public int delete(String statement, Object parameter) {
        AssertUtils.notEmpty(statement, "statement is empty.");

        if (parameter != null) {
            return this.sqlSessionTemplate.delete(statement, parameter);
        } else {
            return this.sqlSessionTemplate.delete(statement);
        }
    }

    /**
     * 批量删除数据的函数。
     * 该方法提供了一种批量删除数据的机制，具有以下特点：
     * 1、默认情况下，每次提交100条数据进行删除。如果在处理过程中发生异常，会继续提交异常行之后的数据，直到全部处理完毕后返回批量处理结果。
     * 2、如果需要支持回滚，即当发生异常时立即抛出数据库异常，而不是等到所有数据都处理完毕后再抛出，可以利用此方法实现。
     *
     * @param statement  指定要执行的SQL语句的标识符。
     * @param objectList 包含需要删除的实体的列表。
     */
    public void batchDelete(String statement, List<?> objectList) {
        batchDelete(statement, objectList, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, false);
    }

    /**
     * 批量删除操作。<br/>
     * 该方法通过提供的SQL语句ID和实体列表，批量删除符合条件的记录。可以选择是否使用批量执行器来优化删除性能。
     *
     * @param statement        表示SQL语句ID，用于指定要执行的删除操作的SQL语句。
     * @param objectList       要删除的实体列表。这些实体的信息将被用于构造删除语句的参数。
     * @param useBatchExecutor 指示是否使用批量执行器来执行删除操作。当设置为true时，将使用批量执行策略，可能提高删除效率。
     */
    public void batchDelete(String statement, List<?> objectList,
                            boolean useBatchExecutor) {
        // 调用另一个重载方法，以默认的刷新大小和指定的是否使用批量执行器来执行批量删除操作
        batchDelete(statement,
                objectList,
                MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE,
                useBatchExecutor);
    }

    /**
     * 批量删除数据的函数。
     *
     * @param statement        指定的SQL语句ID，用于执行删除操作。
     * @param objectList       要删除的实体列表。
     * @param doFlushSize      批量操作时每次提交的大小。如果小于等于0，则使用默认值。
     * @param useBatchExecutor 是否使用批量执行器来执行删除操作。如果为true，则使用批量执行策略；否则，逐个执行删除。
     */
    public void batchDelete(String statement, List<?> objectList,
                            int doFlushSize, boolean useBatchExecutor) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果对象列表为空，则直接返回，不执行任何操作
        if (CollectionUtils.isEmpty(objectList)) {
            return;
        }
        // 如果指定的每次提交大小小于等于0，则使用默认的提交大小
        if (doFlushSize <= 0) {
            doFlushSize = MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE;
        }

        // 根据是否使用批量执行器来选择不同的处理方式
        if (useBatchExecutor) {
            // 使用批量执行器执行删除操作
            SqlHelper.executeBatch(getSqlSessionFactory(), this.log, objectList, doFlushSize, (sqlSession,
                                                                                               entity) -> {
                sqlSession.delete(statement, entity);
            });
        } else {
            // 逐个删除列表中的实体
            for (int index = 0; index < objectList.size(); index++) {
                delete(statement, objectList.get(index));
            }
        }
    }

    /**
     * 更新对象
     * <br/>
     * 根据提供的SQL语句ID和参数，更新数据库中的对象。
     * 如果提供了参数，则使用参数执行更新操作；如果没有提供参数，则仅使用SQL语句执行更新。
     *
     * @param statement 字符串类型的SQL语句ID，用于标识要执行的更新操作。
     * @param parameter 实体实例，可选参数，用于更新操作的参数化绑定。
     * @return 返回更新操作影响的行数。
     */
    public int update(String statement, Object parameter) {
        // 确保SQL语句不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果提供了参数，则使用参数执行更新，否则不使用参数执行更新
        if (parameter != null) {
            return this.sqlSessionTemplate.update(statement, parameter);
        } else {
            return this.sqlSessionTemplate.update(statement);
        }
    }

    /**
     * 更新对象
     * <br/>
     * 根据提供的SQL语句ID、实体实例和条件包装器，更新数据库中的对象。
     *
     * @param statement SQL语句ID，用于标识要执行的更新语句。
     * @param entity    要更新的实体实例，包含更新后的数据。
     * @param wrapper   条件包装器，用于构造更新语句的WHERE子句条件。
     * @param params    可选参数映射，用于传递额外的参数到更新语句中。
     * @return 返回更新操作影响的行数。
     */
    public <T> int update(String statement, T entity, Wrapper<T> wrapper, Map<String, Object> params) {
        // 确保statement、wrapper以及wrapper中的sqlSet不为空
        AssertUtils.notEmpty(statement, "statement is empty.");
        AssertUtils.notTrue(entity == null && wrapper == null, "entity and wrapper is all null.");

        // 初始化或更新params映射，用于传递给更新操作
        params = params == null ? new HashMap<>() : params;
        if (entity != null) {
            params.put(Constants.ENTITY, entity);
        }
        // 将wrapper对象（如果存在）放入params中
        if (wrapper != null) {
            params.put(Constants.WRAPPER, wrapper);
        }
        // 执行更新操作并返回结果
        return this.sqlSessionTemplate.update(statement, params);
    }

    /**
     * 更新对象
     *
     * @param statement              SQL语句标识，用于指定要执行的更新语句。
     * @param entity                 要更新的实体对象，其中的属性将被用于更新操作。
     * @param updateWrapperGenerator 一个函数接口，用于根据实体对象生成更新语句的条件包装器。
     * @param params                 用于更新操作的额外参数，可以为空，如果为空将初始化一个新HashMap。
     * @param <T>                    实体对象的类型。
     * @return 返回更新操作影响的行数。
     */
    public <T> int update(String statement, T entity, Function<T, Wrapper<T>> updateWrapperGenerator,
                          Map<String, Object> params) {
        // 确保基础参数不为空
        AssertUtils.notEmpty(statement, "statement is empty.");
        AssertUtils.notNull(entity, "entity is null.");

        // 处理额外参数
        params = params == null ? new HashMap<>() : params;
        if (entity != null) {
            params.put(Constants.ENTITY, entity);
        }
        // 生成并加入条件包装器
        if (updateWrapperGenerator != null) {
            params.put(Constants.WRAPPER, updateWrapperGenerator.apply(entity));
        }

        // 执行更新操作并返回影响的行数
        return this.sqlSessionTemplate.update(statement, params);
    }

    /**
     * 更新对象
     *
     * @param statement    SQL语句标识，用于指定要执行的更新语句。
     * @param entity       要更新的实体对象，其中的属性将被用于更新操作。
     * @param mapGenerator 一个函数接口，用于根据实体对象生成更新语句的条件包装器。
     * @param <T>          实体对象的类型。
     * @return 返回更新操作影响的行数。
     */
    public <T> int update(String statement, T entity, Function<T, Map<String, Object>> mapGenerator) {
        // 确保基础参数不为空
        AssertUtils.notEmpty(statement, "statement is empty.");
        AssertUtils.notNull(entity, "entity is null.");

        // 处理额外参数
        Map<String, Object> params = mapGenerator != null ? mapGenerator.apply(entity) : new HashMap<>();
        params.put(Constants.ENTITY, entity);

        // 执行更新操作并返回影响的行数
        return this.sqlSessionTemplate.update(statement, params);
    }

    /**
     * 批量更新数据。
     * <p>1、数据批量更新，默认一次提交100条（可通过参数调整），当发生异常后继续提交异常行以后的数据，待集合全部进行提交后返回批量处理结果。</p>
     * <p>2、数据批量更新，如果需要回滚，当发生异常后，数据库异常即向外抛出，不会进行至全部执行后再抛出异常。</p>
     *
     * @param statement        指定的SQL语句ID。
     * @param entityList       实体对象列表，将这些实体对象的数据进行批量更新。
     * @param wrapperGenerator 一个函数，用于为每个实体对象生成对应的更新包装器(Wrapper)。
     * @param params           可选参数映射，可用于定制更新操作或传递给更新语句。
     * @param <T>              实体对象的类型。
     */
    public <T> void batchUpdate(String statement, List<T> entityList,
                                Function<T, Wrapper<T>> wrapperGenerator, Map<String, Object> params) {
        batchUpdate(statement, entityList, wrapperGenerator, params, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, false);
    }

    /**
     * 批量更新数据。本方法提供了一种批量更新数据的机制，具有以下特点：
     * 1、数据批量更新时，默认每次提交100条记录。如果在执行过程中发生异常，会继续提交异常发生行之后的数据，直到全部数据处理完毕后返回批量处理结果。
     * 2、如果需要支持回滚，即当发生异常时立即抛出数据库异常而不等待所有数据执行完毕后再抛出，可以通过参数控制。
     *
     * @param statement        指定的SQL语句ID，用于执行批量更新。
     * @param entityList       要更新的数据实体列表。
     * @param wrapperGenerator 一个函数，用于生成针对每个实体的更新包装器。这允许对每个实体应用特定的更新逻辑。
     * @param params           用于更新操作的额外参数映射。
     * @param useBatchExecutor 是否使用批量执行器。如果为true，则使用批量执行器进行数据更新；否则，按默认方式处理。
     * @param <T>              数据实体的类型。
     */
    public <T> void batchUpdate(String statement, List<T> entityList,
                                Function<T, Wrapper<T>> wrapperGenerator, Map<String, Object> params,
                                boolean useBatchExecutor) {
        batchUpdate(statement,
                entityList, wrapperGenerator, params,
                MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE,
                useBatchExecutor);
    }

    /**
     * 批量更新数据
     *
     * @param statement              执行的SQL语句ID
     * @param entityList             需要更新的实体列表
     * @param updateWrapperGenerator 用于生成包裹实体的函数
     * @param params                 更新操作的额外参数
     * @param doFlushSize            每执行多少条数据后进行一次刷新
     * @param useBatchExecutor       是否使用批量执行器
     */
    public <T> void batchUpdate(String statement, List<T> entityList,
                                Function<T, Wrapper<T>> updateWrapperGenerator, Map<String, Object> params,
                                int doFlushSize, boolean useBatchExecutor) {
        // 确保SQL语句不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果实体列表为空，则直接返回
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        // 如果刷新大小小于等于0，则设置为默认值
        if (doFlushSize <= 0) {
            doFlushSize = MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE;
        }
        // 使用批量执行器进行更新操作
        if (useBatchExecutor) {
            SqlHelper.executeBatch(getSqlSessionFactory(), this.log, entityList, doFlushSize, (sqlSession,
                                                                                               entity) -> {
                Map<String, Object> updateRow = new HashMap<>();
                // 将额外参数和实体加入更新行
                if (MapUtils.isNotEmpty(params)) {
                    updateRow.putAll(params);
                }
                updateRow.put(Constants.ENTITY, entity);
                // 如果存在包裹生成器，则将生成的包裹加入更新行
                if (updateWrapperGenerator != null) {
                    updateRow.put(Constants.WRAPPER, updateWrapperGenerator.apply(entity));
                }
                sqlSession.update(statement, updateRow);
            });
        } else {
            // 不使用批量执行器，逐个更新实体
            for (int index = 0; index < entityList.size(); index++) {
                Map<String, Object> updateRow = new HashMap<>();
                // 将额外参数加入更新行
                if (MapUtils.isNotEmpty(params)) {
                    updateRow.putAll(params);
                }
                update(statement, entityList.get(index), updateWrapperGenerator, updateRow);
            }
        }
    }

    /**
     * 批量更新数据。
     * <p>1、数据批量更新，默认一次提交100条（可通过参数调整），当发生异常后继续提交异常行以后的数据，待集合全部进行提交后返回批量处理结果。</p>
     * <p>2、数据批量更新，如果需要回滚，当发生异常后，数据库异常即向外抛出，不会进行至全部执行后再抛出异常。</p>
     *
     * @param statement    指定的SQL语句ID。
     * @param entityList   实体对象列表，将这些实体对象的数据进行批量更新。
     * @param mapGenerator 一个函数，用于为每个实体对象生成对应的更新包装器(Wrapper)。
     * @param <T>          实体对象的类型。
     */
    public <T> void batchUpdate(String statement, List<T> entityList,
                                Function<T, Map<String, Object>> mapGenerator) {
        batchUpdate(statement, entityList, mapGenerator, MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE, false);
    }

    /**
     * 批量更新数据。本方法提供了一种批量更新数据的机制，具有以下特点：
     * 1、数据批量更新时，默认每次提交100条记录。如果在执行过程中发生异常，会继续提交异常发生行之后的数据，直到全部数据处理完毕后返回批量处理结果。
     * 2、如果需要支持回滚，即当发生异常时立即抛出数据库异常而不等待所有数据执行完毕后再抛出，可以通过参数控制。
     *
     * @param statement        指定的SQL语句ID，用于执行批量更新。
     * @param entityList       要更新的数据实体列表。
     * @param mapGenerator     一个函数，用于生成针对每个实体的更新包装器。这允许对每个实体应用特定的更新逻辑。
     * @param useBatchExecutor 是否使用批量执行器。如果为true，则使用批量执行器进行数据更新；否则，按默认方式处理。
     * @param <T>              数据实体的类型。
     */
    public <T> void batchUpdate(String statement, List<T> entityList,
                                Function<T, Map<String, Object>> mapGenerator,
                                boolean useBatchExecutor) {
        batchUpdate(statement,
                entityList, mapGenerator,
                MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE,
                useBatchExecutor);
    }

    /**
     * 批量更新数据
     *
     * @param statement        执行的SQL语句ID
     * @param entityList       需要更新的实体列表
     * @param mapGenerator     用于生成包裹实体的函数
     * @param doFlushSize      每执行多少条数据后进行一次刷新
     * @param useBatchExecutor 是否使用批量执行器
     */
    public <T> void batchUpdate(String statement, List<T> entityList,
                                Function<T, Map<String, Object>> mapGenerator,
                                int doFlushSize, boolean useBatchExecutor) {
        // 确保SQL语句不为空
        AssertUtils.notEmpty(statement, "statement is empty.");
        // 如果实体列表为空，则直接返回
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        AssertUtils.notNull(mapGenerator, "mapGenerator is empty.");

        // 如果刷新大小小于等于0，则设置为默认值
        if (doFlushSize <= 0) {
            doFlushSize = MybatisGlobalConfig.DEFAULT_DO_FLUSH_SIZE;
        }
        // 使用批量执行器进行更新操作
        if (useBatchExecutor) {
            SqlHelper.executeBatch(getSqlSessionFactory(), this.log, entityList, doFlushSize, (sqlSession,
                                                                                               entity) -> {
                Map<String, Object> updateRow = mapGenerator.apply(entity);
                // 将额外参数和实体加入更新行
                updateRow.put(Constants.ENTITY, entity);
                sqlSession.update(statement, updateRow);
            });
        } else {
            // 不使用批量执行器，逐个更新实体
            for (int index = 0; index < entityList.size(); index++) {
                // 将额外参数加入更新行
                update(statement, entityList.get(index), mapGenerator);
            }
        }
    }


    /**
     * 查询实体对象。<br/>
     * 当不需要参数时，parameter可以传入null。<br/>
     * 该方法通过给定的SQL语句标识符和（可选的）参数来查询单个实体对象。
     *
     * @param statement SQL语句标识符，用于指定要执行的查询。
     * @param parameter 查询参数，如果查询不需要参数，可以传入null。
     * @return 返回查询结果，其类型取决于查询语句和映射配置。
     */
    public <T> T find(String statement, Object parameter) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 根据是否传入parameter参数，选择不同的查询方式
        if (parameter != null) {
            return this.sqlSessionTemplate.<T>selectOne(statement, parameter);
        } else {
            return this.sqlSessionTemplate.<T>selectOne(statement);
        }
    }

    /**
     * <查询实体对象数> <功能详细描述>
     *
     * @param statement [sqlStatementId]
     * @param parameter [实体实例]
     * @return [参数说明]
     * @return int [返回类型说明]
     */
    public int count(String statement, Object parameter) {
        AssertUtils.notEmpty(statement, "statement is empty.");

        if (parameter != null) {
            return (Integer) this.sqlSessionTemplate.selectOne(statement,
                    parameter);
        } else {
            return (Integer) this.sqlSessionTemplate.selectOne(statement);
        }
    }

    /**
     * 查询实体对象的数量。
     *
     * @param statement 指定的SQL语句ID或具体SQL语句。
     * @param wrapper   用于构建查询条件的包装器对象。
     * @param params    包含查询所需额外参数的映射。
     * @return 返回查询到的实体对象数量。
     */
    public int count(String statement, Wrapper wrapper, Map<String, Object> params) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果wrapper为空，则创建一个空的查询条件构建器，否则使用传入的wrapper
        // 如果params为空，则创建一个空的映射，否则使用传入的params
        params = params == null ? new HashMap<>() : params;
        // 将查询条件wrapper放入params映射中
        if (wrapper != null) {
            params.put(Constants.WRAPPER, wrapper);
        }

        // 调用count方法进行查询并返回结果
        return count(statement, params);
    }

    /**
     * 根据给定的SQL语句和参数查询列表对象。
     *
     * @param statement SQL语句标识，用于定位具体的查询语句。
     * @param parameter 查询参数，可以为null。如果非null，则查询时会携带该参数。
     * @return 返回查询结果的列表，列表元素的类型为泛型T。
     */
    public <T> List<T> queryList(String statement, Object parameter) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果parameter不为空，则使用parameter执行查询，否则不带参数执行查询
        if (parameter != null) {
            return this.sqlSessionTemplate.<T>selectList(statement, parameter);
        } else {
            return this.sqlSessionTemplate.<T>selectList(statement);
        }
    }

    /**
     * 根据查询器实例查询对象列表<br/>
     * 该方法通过指定的查询语句和包装器来查询数据库，并返回查询结果的列表。可以额外提供参数映射，用于动态构建查询。
     *
     * @param statement 查询语句的标识符，用于定位具体的查询语句。
     * @param wrapper   查询条件的包装器，用于指定查询的详细条件。如果为null，则默认构建一个空的查询条件包装器。
     * @param <T>       查询结果对象的类型。
     * @return 返回根据查询条件得到的结果列表，类型为List<T>。
     */
    public <T> List<T> queryList(String statement, Wrapper wrapper, Map<String, Object> params) {
        // 确保查询语句不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果未提供wrapper，则默认构建一个空的wrapper；如果未提供params，则新建一个空的map
        params = params == null ? new HashMap<>() : params;
        // 将wrapper对象放入参数映射中，供查询使用
        if (wrapper != null) {
            params.put(Constants.WRAPPER, wrapper);
        }

        // 执行查询，并返回结果列表
        return this.<T>queryList(statement, params);
    }

    /**
     * 查询分页对象<br/>
     * 该方法用于根据给定的SQL语句和参数，查询数据库并返回分页结果集。
     *
     * @param statement 指定的SQL语句ID。
     * @param parameter 查询参数的实体实例。
     * @param pageIndex 请求的页码，从1开始。
     * @param pageSize  每页的记录数。
     * @return 返回一个泛型的分页列表，包含查询结果和分页信息。
     */
    @SuppressWarnings("unchecked")
	public <T> PagedList<T> queryPagedList(String statement, Object parameter,
                                           int pageIndex, int pageSize) {
        // 验证SQL语句不能为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        PagedListImpl<T> result = new PagedListImpl<T>();
        // 验证页码必须大于0
        AssertUtils.isTrue(pageIndex > 0,
                "pageIndex must be integer.but pageIndex now:{}",
                new Object[]{pageIndex});

        // 设置当前页和每页记录数
        result.setCurrent(pageIndex);
        result.setSize(pageSize);

        // 计算总记录数
        String queryCountStatement = statement + "Count";
        int count = (Integer) this.sqlSessionTemplate
                .selectOne(queryCountStatement, parameter);
        result.setTotal(count);
        // 如果总记录数小于等于0，直接返回空的分页结果
        if (count <= 0) {
            return result;
        }
        result.setCount(count);
        result.setSearchCount(false);
        
        // 计算查询的偏移量和限制记录数
        int offset = pageSize * (pageIndex - 1);
        int limit = pageSize * pageIndex;
        limit = Math.min(limit, count); // 保证limit不超过总记录数
        if(parameter instanceof Map) {
        	((Map<String, Object>)parameter).put("ipage", result);
        }
        // 执行查询，并设置查询结果
		/*
		 * List<T> list = this.sqlSessionTemplate.<T>selectList(statement, parameter,
		 * new RowBounds(offset, limit));
		 */
        List<T> list = this.sqlSessionTemplate.<T>selectList(statement,
                parameter);
        result.setRecords(list);
        return result;
    }

    /**
     * 查询分页对象<br/>
     * 该方法用于根据给定的SQL语句、查询条件、分页信息等，查询数据库并返回分页结果。
     *
     * @param statement [sqlStatementId] SQL语句标识符，用于查询数据。
     * @param params    查询参数，可以包含各种需要的参数信息。
     * @param wrapper   包装器，用于包装查询条件，可以为null，若为null则默认创建一个空的包装器。
     * @param pageIndex [当前页码，从0开始]，用于指定需要查询的页码。
     * @param pageSize  [一页显示实例数]，用于指定每页显示的数据数量。
     * @param <T>       泛型参数，指定返回结果的数据类型。
     * @return [返回分页对象PagedList<T>] 包含当前页数据和分页信息的对象。
     */
    public <T> PagedList<T> queryPagedList(String statement, Wrapper wrapper, Map<String, Object> params,
                                           int pageIndex,
                                           int pageSize) {
        // 确保SQL语句不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 处理查询参数，如果未提供，则初始化为空Map，并添加包装器作为参数
        params = params == null ? new HashMap<>() : params;
        if (wrapper != null) {
            params.put(Constants.WRAPPER, wrapper);
        }

        // 执行分页查询，并返回结果
        return this.<T>queryPagedList(statement, params, pageIndex, pageSize);
    }

    /**
     * 查询分页对象。该方法根据提供的SQL语句、参数、页码和每页数量来查询数据，并返回一个分页列表。
     * 如果传入的count小于等于0或当前页已经是最后一页，则会按照给定的count进行分页处理。
     *
     * @param statement SQL语句的标识符。
     * @param parameter 执行查询时传递给SQL语句的参数实体。
     * @param pageIndex 当前页码。
     * @param pageSize  每页显示的数量。
     * @param count     总记录数。如果小于等于0，则不进行分页计算。
     * @return 返回一个泛型的分页列表，包含当前页的数据。
     */
    @SuppressWarnings("unchecked")
	public <T> PagedList<T> queryPagedList(String statement, Object parameter,
                                           int pageIndex, int pageSize, int count) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        PagedListImpl<T> result = new PagedListImpl<T>();
        
        // 设置当前页和每页记录数
        result.setCurrent(pageIndex);
        result.setSize(pageSize);

        // 设置总记录数，如果count小于等于0，则不进行分页计算
        result.setTotal(count);
        if (count == 0) {
            return result; // 当总记录数为0时，直接返回空的分页结果
        }
        result.setSearchCount(false);

        // 计算查询的偏移量和限制数量
        int offset = pageSize * (pageIndex - 1);
        int limit = pageSize * pageIndex;
        limit = Math.min(limit, count); // 保证limit不超过总记录数
        if(parameter instanceof Map) {
        	((Map<String, Object>)parameter).put("ipage", result);
        }
        // 执行查询，并设置查询结果到分页列表
		/*
		 * List<T> list = this.sqlSessionTemplate.<T>selectList(statement, parameter,
		 * new RowBounds(offset, limit));
		 */
        List<T> list = this.sqlSessionTemplate.<T>selectList(statement,
                parameter);
        result.setList(list);
        return result;
    }

    /**
     * 查询分页对象。该方法用于根据给定的SQL语句、参数、分页信息等，查询数据库并返回分页结果。
     *
     * @param statement 指定的SQL语句ID。
     * @param params    查询所需的参数集合。
     * @param wrapper   包装器对象，用于包装查询条件。
     * @param pageIndex 当前页码。
     * @param pageSize  每页的记录数。
     * @param count     查询总记录数。
     * @return 返回一个泛型的分页列表对象PagedList<T>，其中包含查询结果及分页信息。
     */
    public <T> PagedList<T> queryPagedList(String statement, Wrapper wrapper, Map<String, Object> params,
                                           int pageIndex,
                                           int pageSize,
                                           int count) {
        // 确保SQL语句不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果参数集合为空，则初始化一个新的空参数集合
        params = params == null ? new HashMap<>() : params;
        // 如果包装器对象为空，则创建一个新的默认包装器对象
        // 将包装器对象添加到参数集合中
        if (wrapper != null) {
            params.put(Constants.WRAPPER, wrapper);
        }

        // 执行分页查询，并返回结果
        return this.<T>queryPagedList(statement,
                params,
                pageIndex,
                pageSize,
                count);
    }

    /**
     * 根据给定的SQL语句和参数，查询列表数据并将其映射为一个Map。
     *
     * @param statement 指定的SQL语句ID。
     * @param parameter 用于查询的实体实例，可以为null。
     * @param mapKey    映射到Map中的键名。
     * @return 返回一个Map对象，其中键为指定的mapKey，值为查询结果。
     */
    public <K, V> Map<K, V> queryMap(String statement, Object parameter,
                                     String mapKey) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 如果parameter不为null，则使用parameter和mapKey执行查询并映射结果
        if (parameter != null) {
            return this.sqlSessionTemplate
                    .<K, V>selectMap(statement, parameter, mapKey);
        } else {
            // 如果parameter为null，只使用statement和mapKey执行查询并映射结果
            return this.sqlSessionTemplate.<K, V>selectMap(statement, mapKey);
        }
    }

    /**
     * 根据给定的SQL语句和参数，查询列表数据并以Map形式返回。该方法支持分页查询。
     *
     * @param statement 指定的SQL语句ID。
     * @param parameter 用于查询的参数实体实例。
     * @param mapKey    用于将查询结果映射到Map中的键名。
     * @param pageIndex 当前页码，从1开始。
     * @param pageSize  每页显示的记录数。
     * @return 返回一个Map对象，其中键为mapKey指定的字段值，值为对应的实体对象。
     */
    public <K, V> Map<K, V> queryMap(String statement, Object parameter,
                                     String mapKey, int pageIndex, int pageSize) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 计算查询的起始位置和结束位置（即分页）
        int offset = pageSize * (pageIndex - 1);
        int limit = pageSize * pageIndex;

        // 执行查询，并将结果映射为Map返回
        return this.sqlSessionTemplate.<K, V>selectMap(statement,
                parameter,
                mapKey,
                new RowBounds(offset, limit));
    }

    /**
     * 执行查询操作。
     *
     * @param statement 表示SQL语句的ID或具体的SQL语句。
     * @param parameter 执行查询时需要传递的参数，可以是任何对象，如果为null，则不传递参数。
     * @param handler   处理查询结果的回调接口。
     *                  <p>
     *                  该方法根据传入的statement和parameter执行查询操作，并通过handler处理查询结果。
     *                  如果parameter不为null，则将parameter作为参数传递给SQL语句。
     */
    public void query(String statement, Object parameter,
                      ResultHandler<?> handler) {
        // 确保statement不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 根据parameter是否为null，选择不同的查询方式
        if (parameter != null) {
            this.sqlSessionTemplate.select(statement, parameter, handler);
        } else {
            this.sqlSessionTemplate.select(statement, handler);
        }
    }


    /**
     * 执行查询操作。
     *
     * @param statement 表示SQL语句的ID或具体SQL语句，用于指定要执行的查询。
     * @param parameter 用于查询的参数，可以是一个实体实例，根据实际的查询需求而定。
     * @param rowBounds 用于分页查询的参数，指定查询结果的起始位置和返回记录数。
     * @param handler   结果处理器，用于处理查询结果。
     *                  <p>
     *                  该方法通过提供的SQL语句ID、参数、分页信息和结果处理器来执行查询，并利用SQLSessionTemplate的select方法来完成数据库查询操作。
     */
    public void query(String statement, Object parameter, RowBounds rowBounds,
                      ResultHandler<?> handler) {
        // 确保查询语句不为空
        AssertUtils.notEmpty(statement, "statement is empty.");

        // 执行查询操作
        this.sqlSessionTemplate
                .select(statement, parameter, rowBounds, handler);
    }


    /**
     * 批量提交更新删除等操作的批次提交功能。
     * 当处理大量数据列表时，调用此方法来提交所有待处理的SQL语句。
     * 提供给用户自行控制批量执行的灵活性，仅在批量操作模式下有效，非批量操作不受此方法影响。
     */
    public void flush() {
        this.sqlSessionTemplate.flushStatements(); // 提交当前批次的所有SQL语句
    }


    /**
     * 清空缓存
     * <br/>
     * 该方法无参数。
     * <br/>
     * 该方法没有返回值。
     */
    public void clearCache() {
        this.sqlSessionTemplate.clearCache(); // 清除SqlSessionTemplate的缓存
    }


    public PersistenceExceptionTranslator getExceptionTranslator() {
        return this.sqlSessionTemplate.getPersistenceExceptionTranslator();
    }

    public SqlSessionFactory getSqlSessionFactory() {
        return this.sqlSessionTemplate.getSqlSessionFactory();
    }

    public Configuration getConfiguration() {
        return getSqlSessionFactory().getConfiguration();
    }

    public SqlSessionTemplate getSqlSessionTemplate() {
        return sqlSessionTemplate;
    }

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    /**
     * 为对象填入UUID
     *
     * @param parameter [实体实例]
     */
    private static void autoSetEntityPKWithGenerator(Object parameter, String pkPropertyName) {
        if (StrUtil.isEmpty(pkPropertyName)) {
            pkPropertyName = TableInfoUtils.parsePKTableColumn(parameter.getClass()).getPropertyName();
        }
        BeanWrapper bw = PropertyAccessorFactory
                .forBeanPropertyAccess(parameter);
        PropertyDescriptor pd = bw.getPropertyDescriptor(pkPropertyName);
        AssertUtils.isTrue(pd.getWriteMethod() != null && pd.getReadMethod() != null,
                "指定主键错误，或主键的get,set方法不存在.class:{} | pkPropertyName:{}",
                new Object[]{parameter.getClass(), pkPropertyName});

        //如果存在TableId的注解则不执行自动写入主键值的逻辑
        TypeDescriptor pkTD = bw.getPropertyTypeDescriptor(pkPropertyName);
        if (pkTD == null || pkTD.hasAnnotation(TableId.class)) {
            return;
        }

        //没有TableId注解则自动写入主键
        if (ObjectUtil.isEmpty(bw.getPropertyValue(pkPropertyName))) {
            Class<?> pkPropertyType = pd.getPropertyType();
            if (String.class.equals(pkPropertyType)) {
                String uuid = uuidGenerator.next().replace("-", "");
                bw.setPropertyValue(pkPropertyName, uuid);
                return;
            } else if (long.class.equals(pkPropertyType) || Long.class.equals(pkPropertyType)) {
                long uuid = snowflakeGenerator.next();
                bw.setPropertyValue(pkPropertyName, uuid);
                return;
            } else if (int.class.equals(pkPropertyType) || Integer.class.equals(pkPropertyType)) {
                //一般来说不建设弄int类型的主键，除非确定其数据经过非常的长期都不会有啥变化的情形
                long uuid = snowflakeGenerator.next();
                bw.setPropertyValue(pkPropertyName, (int) uuid);
                return;
            } else {
                throw new SILException(MessageUtils.format("不支持的主键类型.class:{} | pkPropertyName:{} | pkType:{}",
                        new Object[]{parameter.getClass(), pkPropertyName}));
            }
        }
    }
}
